﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;

namespace EntityFrameworkContrib.PostSharp4EF
{
    /// <summary>
    /// interface that can be used by the circular weaver to 
    /// store and retrieve original values
    /// </summary>
	public interface ISerializationHelp
	{
        /// <summary>
        /// Gets the original value.
        /// </summary>
        /// <param name="Propertyname">The propertyname.</param>
        /// <returns></returns>
        object GetOriginalValue(string Propertyname);

        /// <summary>
        /// Sets the value as original.
        /// </summary>
        /// <param name="Propertyname">The propertyname.</param>
        /// <param name="value">The value.</param>
        void SetValueAsOriginal(string Propertyname, object value);

        /// <summary>
        /// Begins the deserialization.
        /// </summary>
        void BeginDeserialization();

        /// <summary>
        /// Ends the deserialization.
        /// </summary>
        void EndDeserialization();

        /// <summary>
        /// Determines whether this instance is deserializing.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance is deserializing; otherwise, <c>false</c>.
        /// </returns>
        bool IsDeserializing();

        /// <summary>
        /// Shoulds the original value be serialized
        /// </summary>
        /// <param name="Propertyname">The propertyname.</param>
        /// <returns></returns>
        bool ShouldSerializeOriginalValue(string Propertyname);
	}

    /// <summary>
    /// hooks up the interface implementation to the instance
    /// </summary>
    [Serializable]
    public class SerializationHelpSubAspect : CompositionAspect
    {

        public override object CreateImplementationObject(InstanceBoundLaosEventArgs eventArgs)
        {
            return new SerializationHelpImplementation(eventArgs.Instance);
        }

        public override Type GetPublicInterface(Type containerType)
        {
            return typeof(ISerializationHelp);
        }

        public override CompositionAspectOptions GetOptions()
        {
            return CompositionAspectOptions.IgnoreIfAlreadyImplemented;
        }
    }

    /// <summary>
    /// implements ISerializationHelp 
    /// </summary>
    [Serializable]
    public class SerializationHelpImplementation : ISerializationHelp
    {
        #region fields and properties
        /// <summary>
        /// the instance we implement for
        /// </summary>
        IStorageAccess instance;

        /// <summary>
        /// determines whether we are deserializing
        /// </summary>
        bool Deserializing = false; 
        #endregion

        #region ctor
        /// <summary>
        /// Initializes a new instance of the <see cref="SerializationHelpImplementation"/> class.
        /// </summary>
        /// <param name="instance">The instance.</param>
        public SerializationHelpImplementation(object instance)
        {
            this.instance = (IStorageAccess)instance;
        } 
        #endregion

        #region ISerializationHelp Members

        /// <summary>
        /// Gets the original value.
        /// </summary>
        /// <param name="Propertyname">The propertyname.</param>
        /// <returns></returns>
        public object GetOriginalValue(string Propertyname)
        {
            return instance.GetValue("OriginalValue_" + Propertyname);
        }

        /// <summary>
        /// Sets the value as original.
        /// </summary>
        /// <param name="Propertyname">The propertyname.</param>
        /// <param name="value">The value.</param>
        public void SetValueAsOriginal(string Propertyname, object value)
        {
            /* we can check for null here and stop it if it is
             the original values are always initialized to their default values
             by the compiler, so no need to do that ourselves */

            // TODO: surprised to see dbnulls getting to here

            if (value != null && value != DBNull.Value)
            {
                instance.SetValue("OriginalValue_" + Propertyname, value);
            }
        }

        /// <summary>
        /// Begins the deserialization.
        /// </summary>
        public void BeginDeserialization()
        {
            Deserializing = true;
        }

        /// <summary>
        /// Ends the deserialization.
        /// </summary>
        public void EndDeserialization()
        {
            Deserializing = false;
        }


        /// <summary>
        /// Determines whether this instance is deserializing.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance is deserializing; otherwise, <c>false</c>.
        /// </returns>
        public bool IsDeserializing()
        {
            return Deserializing;
        }

        /// <summary>
        /// Shoulds the original value be serialized
        /// </summary>
        /// <param name="Propertyname">The propertyname.</param>
        /// <returns></returns>
        public bool ShouldSerializeOriginalValue(string Propertyname)
        {
            return true;
        }

        #endregion
    }
}
